package com.kk.demo.concurrent;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong;

/**
 * AtomicStampedReference进行ABA的问题
 * 
 *  在运用CAS做Lock-Free操作中有一个经典的ABA问题：
 *  线程1准备用CAS将变量的值由A替换为B，
 *  在此之前，线程2将变量的值由A替换为C，又由C替换为A，
 *  然后线程1执行CAS时发现变量的值仍然为A，所以CAS成功。
 *  但实际上这时的现场已经和最初不同了，尽管CAS成功，但可能存在潜藏的问题
 *  
 *  专门为此问题而进行修改
 * 
* 源文件名：UserAtomicStampedReferenceDemo.java
* 文件版本：1.0.0
* 创建作者：liujun
* 创建日期：2016年11月11日
* 修改作者：liujun
* 修改日期：2016年11月11日
* 文件描述：TODO
* 版权所有：Copyright 2016 zjhz, Inc. All Rights Reserved.
*/
public class UserAtomicReferenceDemo {

    final static long INITVALUE = 10l;

    /**
     * 摸拟用户帐户,现帐户上有100元钱,
     * 比如，一次充值，被两个线程执行，线程1先充值，被立即消费了，线程2来进行充值，
     * 发现还是原来的值，就又发生了一次充值，发生了重复充值的问题.导致了损失
    * @字段说明 AtomicLong
    */
    private static AtomicLong atomicLong = new AtomicLong(INITVALUE);

    /**
     * 线程池对象
    * @字段说明 execut
    */
    private static final ExecutorService EXEC_RUN = Executors.newFixedThreadPool(10);

    /**
     * 充值
    * 方法描述
    * @param oldMoney
    * @创建日期 2016年11月11日
    */
    public void addMoneyValue(long initValue, long addValue) {
        if (atomicLong.compareAndSet(initValue, (initValue + addValue))) {
            System.out.println("充值成功为:" + (initValue + addValue));
        }
    }

    public void userMoneyValue(long currValue, long inctvalue) {
        if (atomicLong.compareAndSet(currValue, (currValue - inctvalue))) {
            System.out.println("消费结果为:" + (currValue - inctvalue));
        }
    }

    public static void main(String[] args) {

        UserAtomicReferenceDemo atom = new UserAtomicReferenceDemo();
        // 进行充值
        EXEC_RUN.submit(() -> {
            // 首先充值10元
            atom.addMoneyValue(INITVALUE, 10);

            // 进行消息费，再对此进行充值
            atom.userMoneyValue(INITVALUE + 10, INITVALUE);
        });

        // 进行消费
        EXEC_RUN.submit(() -> {

            try {
                Thread.currentThread().sleep(1000l);
            } catch (Exception e) {
                e.printStackTrace();
            }
            // 被另外的线程消费j
            atom.addMoneyValue(INITVALUE, 10);
            System.out.println("可以被反复的充值的问题");
        });
    }

}
